package com.agileplex.products.acc;

import java.util.Iterator;
import java.util.List;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

import com.agileplex.products.acc.commands.Command;
import com.agileplex.products.acc.commands.LexicalParser;
import com.agileplex.products.acc.results.ICommandResult;
import com.agileplex.products.acc.results.ExitSession;
import com.agileplex.products.acc.results.CommandNotFound;

/**
 * ACC session.
 *
 * @creator Rui Tang
 * @date 2008-06-14
 *
 * @LastChanged
 * @LastChangedBy $LastChangedBy: tangrui@agileplex.com $
 * @LastChangedDate $LastChangedDate: 2008-06-22 16:08:49 +0000 (Sun, 22 Jun 2008) $
 * @LastChangedRevision $LastChangedRevision: 7 $
 */
public class Session {

    private Configuration config;

    private InputStream in;
    private OutputStream out;

    private PrintWriter writer;
    private BufferedReader reader;
    private ICommandResult result;

    public Session(Configuration config) {
        this.config = config;
    }

    public String getPrompt() {
        return this.config.getPrompt();
    }

    public InputStream getInputStream() {
        return this.in;
    }

    public OutputStream getOutputStream() {
        return this.out;
    }

    public void open(InputStream in, OutputStream out) throws IOException, CommandExecutionException {
        this.in = in;
        this.out = out;

        this.reader = new BufferedReader(new InputStreamReader(in));
        this.writer = new PrintWriter(out, true);
        
        getSessionStack().push(this);
        this.process();
    }

    private void process() throws IOException, CommandExecutionException {
        ICommandResult result = null;
        if (reader.ready()) {
            while (reader.ready()) {
                result = this.exec(reader.readLine());
                result.print(this.writer);
                this.writer.flush();
            }
        } else {
            do {
                this.writer.print(getSessionStack().getDedicatedPrompt());
                this.writer.flush();

                result = this.exec(reader.readLine());
                result.print(this.writer);
                this.writer.flush();
            } while (result != ExitSession.getSingleton());
        }
    }

    private ICommandResult exec(String cmdStr) throws CommandExecutionException {
        //LiteralCommand literal = new LiteralCommand(cmdStr);
        LexicalParser lexicalParser = new LexicalParser(cmdStr);
        String token = lexicalParser.getNextToken();
        List<Command> cmds = config.getCommands();
        for (Iterator<Command> it = cmds.iterator(); it.hasNext(); ) {
            Command cmd = it.next();
            if (token.equals(cmd.getName())) {
                return cmd.exec(lexicalParser, this);
            }
            /*if (literal.startsWith(cmd.getName())) {
                return cmd.exec(literal.getRest(cmd.getName()), this);
            }*/
        }

        if (Constants.DEFAULT_EXIT_COMMAND.equals(cmdStr)) {
            return this.exit();
        }

        return CommandNotFound.getSingleton();
    }

    private ICommandResult exit() {
        getSessionStack().pop();
        return ExitSession.getSingleton();
    }

    private static SessionStack getSessionStack() {
        return SessionStack.getSingleton();
    }

}
